#ifndef _CACHE_CONTROLLER_H_
#define _CACHE_CONTROLLER_H_

#include "systemc.h"

class cache_controller : sc_module
{
  sc_lv<32> _c_dout;
  sc_lv<32> _wv_addr;
  sc_lv<32> _wb_addr;


  /* victim cache - 
   *   hit  : 11;
   *   miss : 01;
   *   wb   : 10;
   *
   *   NOTE: 00 is invalid and considered miss;
   */
  sc_lv<2>  _v_hmw;

  /* write cache buffer. filled during read victim cache. */
  sc_lv<32> _wc_buffer[4];

  sc_logic _din_src;
  sc_logic _block;

public:
  /* SIGNALS */
  /* CPU */
  sc_in< sc_logic >   cs;
  sc_in< sc_logic >   rw;
  sc_in< sc_lv<32> >  addr;
  sc_in< sc_lv<32> >  din;

  sc_out< sc_logic  > rdy;
  sc_out< sc_lv<32> > dout;

  /* RAM */
  sc_in< sc_logic  >  m_rdy;
  sc_in< sc_lv<32> >  m_dout0;
  sc_in< sc_lv<32> >  m_dout1;
  sc_in< sc_lv<32> >  m_dout2;
  sc_in< sc_lv<32> >  m_dout3;

  sc_out< sc_logic >  m_cs;
  sc_out< sc_logic >  m_rw;
  sc_out< sc_lv<32> > m_addr;
  sc_out< sc_lv<32> > m_din0;
  sc_out< sc_lv<32> > m_din1;
  sc_out< sc_lv<32> > m_din2;
  sc_out< sc_lv<32> > m_din3;

  /* VICTIM CACHE*/
  sc_in< sc_logic >   v_rdy;
  sc_in< sc_logic >   v_hit;
  sc_in< sc_lv<30> >  v_tout;
  sc_in< sc_lv<32> >  v_dout0;
  sc_in< sc_lv<32> >  v_dout1;
  sc_in< sc_lv<32> >  v_dout2;
  sc_in< sc_lv<32> >  v_dout3;

  sc_out< sc_logic >  v_cs;
  sc_out< sc_logic >  v_rw;
  sc_out< sc_lv<32> > v_addr;
  sc_out< sc_lv<32> > v_din0;
  sc_out< sc_lv<32> > v_din1;
  sc_out< sc_lv<32> > v_din2;
  sc_out< sc_lv<32> > v_din3;
  sc_out< sc_logic >  v_set_v;   
  sc_out< sc_logic >  v_set_d;

  /* CACHE */
  sc_in< sc_logic >   c_rdy;
  sc_in< sc_logic >   c_hit;
  sc_in< sc_lv<25> >  c_tout;
  sc_in< sc_lv<32> >  c_dout0;
  sc_in< sc_lv<32> >  c_dout1;
  sc_in< sc_lv<32> >  c_dout2;
  sc_in< sc_lv<32> >  c_dout3;

  sc_out< sc_logic >  c_cs;
  sc_out< sc_logic >  c_rw;
  sc_out< sc_lv<32> > c_addr;
  sc_out< sc_lv<32> > c_din0;
  sc_out< sc_lv<32> > c_din1;
  sc_out< sc_lv<32> > c_din2;
  sc_out< sc_lv<32> > c_din3;
  sc_out< sc_logic >  c_set_v;   
  sc_out< sc_logic >  c_din_src;
  sc_out< sc_logic >  c_block;

  /* STATE */
  sc_out< sc_lv<3> >  state;
  
  sc_signal< sc_logic > busy;

  SC_CTOR (cache_controller)
    : _c_dout(0), _wv_addr(0), _v_hmw(0), _wb_addr(0), _din_src(SC_LOGIC_0),
      _block(SC_LOGIC_0)
  {
    for (int i = 0; i < 4; i++)
      _wc_buffer[i] = 0;

    rdy.initialize(SC_LOGIC_1);
    dout.initialize(0);
    m_cs.initialize(SC_LOGIC_0);
    m_rw.initialize(SC_LOGIC_0);
    m_addr.initialize(0);
    m_din0.initialize(0);
    m_din1.initialize(0);
    m_din2.initialize(0);
    m_din3.initialize(0);
    v_cs.initialize(SC_LOGIC_0);
    v_rw.initialize(SC_LOGIC_0);
    v_addr.initialize(0);
    v_din0.initialize(0);
    v_din1.initialize(0);
    v_din2.initialize(0);
    v_din3.initialize(0);
    v_set_v.initialize(SC_LOGIC_0);
    v_set_d.initialize(SC_LOGIC_0);
    c_cs.initialize(SC_LOGIC_0);
    c_rw.initialize(SC_LOGIC_0);
    c_addr.initialize(0);
    c_din0.initialize(0);
    c_din1.initialize(0);
    c_din2.initialize(0);
    c_din3.initialize(0);
    c_set_v.initialize(SC_LOGIC_0);
    c_din_src.initialize(SC_LOGIC_0);
    c_block.initialize(SC_LOGIC_0);

    state.initialize(0);

    SC_THREAD (business);
    sensitive << state;

    SC_THREAD (fsm);
    sensitive << cs << rw << addr
              << c_rdy << m_rdy << v_rdy
              << busy;

    SC_METHOD (fsm_logic);
    sensitive << state;
  }

  void business(void);
  void fsm(void);
  void fsm_logic(void);

};

#endif /* _CACHE_CONTROLLER_H_ */
